#include <pthread.h>
#include <iostream>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <deque>
#include <errno.h>
#include <unistd.h>
#include <fstream>
#include "boost/date_time/posix_time/posix_time.hpp"


std::deque<int> sockQueue;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int tempSock = 0;
int* sockRefer;

std::ofstream logg("log.txt", std::ios_base::app);
pthread_mutex_t mutexLog = PTHREAD_MUTEX_INITIALIZER;

int threadNumber = 1;
pthread_mutex_t mutexThreadNumber = PTHREAD_MUTEX_INITIALIZER;

int workingThread = 0;
pthread_mutex_t mutexWorkingThread = PTHREAD_MUTEX_INITIALIZER; 

void work()
{
	time_t seconds;
  	seconds = time (NULL);
  	while ((time(NULL) - seconds) < 5)
  	{
	  	char * ptr = new char[50];
	  	for(int i=0; i<50; ++i)
	  		ptr[i] = 0;
	  	delete [] ptr;
  	}
}

void * threadCreate(void *)
{
   static size_t enter = 0;
   int sock = 0;
   char message[512];
   while(true)
   {	
		pthread_mutex_lock( &mutex);

	   	if (!sockQueue.empty())
	   	{		   			
			sock = sockQueue.front();
			std::cout << "tempSock: " << sock << std::endl;
			sockQueue.pop_front();			
			pthread_mutex_unlock( &mutex);
			
			bool workingThreadIsNullAtBegin;
			pthread_mutex_lock( &mutexWorkingThread);
			if (!workingThread)
			{
				workingThreadIsNullAtBegin = true;
				++workingThread;
			}			
			pthread_mutex_unlock( &mutexWorkingThread);
			
			pthread_mutex_lock( &mutexThreadNumber);
			int localThreadNumber = threadNumber;
			++threadNumber;
			pthread_mutex_unlock( &mutexThreadNumber);			
			
			pthread_mutex_lock( &mutexLog);
			boost::posix_time::ptime timeStart(boost::posix_time::microsec_clock::local_time());			
			if (workingThreadIsNullAtBegin)
				logg <<localThreadNumber<< " thread " << " beg: " << timeStart << std::endl;				
			pthread_mutex_unlock( &mutexLog);			
			
			int nbytes = recv(sock, message, 512, 0);
			
			work();

			if (nbytes < 0)
			{
				printf("error %d bytes=%d\n", __LINE__,nbytes);
				std::cout << "errno=" << errno << std::endl;
				//exit(1);
			}

			printf ("Server: got message: %s\n", message);

			if(send(sock, message, strlen(message)+1, 0) < 0)
			{
			   	 printf("error %d\n",__LINE__);			 
				// exit(1);          	
			}			
			
			pthread_mutex_lock( &mutexLog);
			
			bool workingThreadIsNullAtEnd;
			pthread_mutex_lock( &mutexWorkingThread);
			if (workingThread == 1)
			{
				workingThreadIsNullAtEnd = true;
				--workingThread;
			}			
			pthread_mutex_unlock( &mutexWorkingThread);
	   		
			boost::posix_time::ptime timeFinish(boost::posix_time::microsec_clock::local_time());
			if (workingThreadIsNullAtEnd)
				logg<< localThreadNumber <<" thread "<<" end: "<<timeFinish <<std::endl;
			pthread_mutex_unlock( &mutexLog);		 
			
		}
		else 
		{
			pthread_mutex_unlock(&mutex);
			usleep(100);
		}
   }	
        
}

int main(int argc, char *argv[])
{	
	int sock = socket(AF_INET, SOCK_STREAM, 0);
	int newsock = 0;

    struct sockaddr_in saServer, saClient;
    struct hostent * my_hostent;
    my_hostent = gethostbyname("localhost");
    int i = 0;
    while (my_hostent -> h_addr_list[i] != NULL) 
    {
        printf( "%s\n", inet_ntoa( *( struct in_addr*)( my_hostent-> h_addr_list[i])));
        i++;
    }

    saServer.sin_family = AF_INET; 
    saServer.sin_addr = *( struct in_addr*)( my_hostent-> h_addr_list[0]);
    saServer.sin_port = htons(10000);

    if (bind(sock, (sockaddr*) &saServer, sizeof(saServer)) != 0)
    {
    	printf("couldn't bind socket\n");
    }
    
    size_t size = sizeof (saServer);	

	if(listen(sock, 10) < 0)
	{
		printf("couldn't do 'listen'\n");
		return 1;
    	}

	pthread_t threads[10];
	for (size_t i = 0; i < 10; ++i)
		pthread_create (&threads[i], NULL, threadCreate, NULL);	    	    	 

	socklen_t clientLen = 0;	
	
	while(true)
	{   
		clientLen = sizeof(saClient);     
		if((newsock = accept(sock,(struct sockaddr *) & saClient, &clientLen)) < 0)
		{
		    printf("couldn't do 'accept'\n");
			return 1;
       	}
		printf("newsock: %i\n", newsock);

		
		if (newsock > 0)
		{

		   	pthread_mutex_lock( &mutex);
			if (sockQueue.size() < 20)						
				sockQueue.push_back(newsock);		
			else
				close(newsock);
			pthread_mutex_unlock(&mutex);
		}
	}
	
	//for (size_t i = 0; i < 10; ++i)
		//pthread_join (threads[i], NULL);
	
        return 0;
}

